using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Web.Services.Description;
using System.Web.Services.Discovery;
using System.IO;
using System.Collections;

namespace MH.WsdlWorks.ProjectModel
{
    [XmlType]
    public class Operation : DirectoryNode
    {
        public Operation() { }
        public Operation(Wsdl parentWsdl, OperationBinding wsdlOperation)
            : base(parentWsdl, wsdlOperation.Name)
        {
            this.serviceUrl = parentWsdl.ServiceUrl;
            this.soapAction = GetSoapAction(wsdlOperation);

            // add an initial request
            AddRequest();
        }

        private static string GetSoapAction(OperationBinding wsdlOperation)
        {
            foreach (object extension in wsdlOperation.Extensions)
            {
                SoapOperationBinding soapOperationBinding = extension as SoapOperationBinding;
                if (soapOperationBinding != null)
                {
                    return soapOperationBinding.SoapAction;
                }
            }
            throw new ApplicationException(string.Format("No soap action found for operation: '{0}'", wsdlOperation.Name));
        }

        public Request AddRequest()
        {
            string requestName = "Request";
            Request request = new Request(this, requestName, GetInputMessageSchemaInfo());
            this.Requests.Add(request);
            return request;
        }

        List<Request> requests = new List<Request>();
        [XmlArray]
        public List<Request> Requests
        {
            get { return requests; }
            set { requests = value; }
        }

        string serviceUrl;
        [XmlAttribute]
        public string ServiceUrl
        {
            get { return serviceUrl; }
            set { serviceUrl = value; }
        }

        string soapAction;
        [XmlAttribute]
        public string SoapAction
        {
            get { return soapAction; }
            set { soapAction = value; }
        }

        public override void SetParent(INode parent)
        {
            this.Parent = parent;
            foreach (Request request in this.requests)
            {
                request.SetParent(this);
            }
        }

        /// <summary>
        /// Loads the WSDL file and finds the input message definition for this operation
        /// </summary>
        /// <returns></returns>
        public MessageSchemaInfo GetInputMessageSchemaInfo()
        {
            MessageSchemaInfo messageSchemaInfo = new MessageSchemaInfo();

            string wsdlMapFilePath = ((Wsdl)this.Parent).WsdlMapFile.GetPath();
            DiscoveryClientProtocol client = new DiscoveryClientProtocol();
            client.ReadAll(wsdlMapFilePath);

            LoadSchemas(messageSchemaInfo, client);

            System.Web.Services.Description.Operation operation = GetOperation(client, this.Name);
            string inputMessageName = operation.Messages.Input.Message.Name;
            Message message = GetMessage(client, inputMessageName);
            messageSchemaInfo.RootElement = message.Parts[0].Element;
            return messageSchemaInfo;
        }

        private System.Web.Services.Description.Operation GetOperation(DiscoveryClientProtocol client, string operationName)
        {
            foreach (DictionaryEntry entry in client.Documents)
            {
                ServiceDescription serviceDescription = entry.Value as ServiceDescription;
                if ((serviceDescription != null) && (serviceDescription.PortTypes.Count != 0))
                {
                    foreach (System.Web.Services.Description.Operation operation in 
                        serviceDescription.PortTypes[0].Operations)
                    {
                        if (operation.Name == operationName)
                        {
                            return operation;
                        }
                    }
                }
            }
            throw new ApplicationException(string.Format("Could not find operation {0} in WSDL", operationName));
        }

        private Message GetMessage(DiscoveryClientProtocol client, string inputMessageName)
        {
            foreach (DictionaryEntry entry in client.Documents)
            {
                ServiceDescription serviceDescription = entry.Value as ServiceDescription;
                if ((serviceDescription != null) && (serviceDescription.Messages[inputMessageName] != null))
                {
                    return serviceDescription.Messages[inputMessageName];
                }
            }
            throw new ApplicationException("No message definitions found in WSDL");
        }

        private void LoadSchemas(MessageSchemaInfo messageSchemaInfo, DiscoveryClientProtocol client)
        {
            foreach (DictionaryEntry entry in client.Documents)
            {
                ServiceDescription serviceDescription = entry.Value as ServiceDescription;
                if ((serviceDescription != null))
                {
                    messageSchemaInfo.AddSchemas(serviceDescription.Types.Schemas);
                }
                System.Xml.Schema.XmlSchema schema = entry.Value as System.Xml.Schema.XmlSchema;
                if ((schema != null))
                {
                    messageSchemaInfo.AddSchema(schema);
                }
            }
        }
    }

    public class MessageSchemaInfo
    {
        public XmlQualifiedName RootElement;
        public XmlSchemaSet Schemas = new XmlSchemaSet();

        public void AddSchemas(XmlSchemas xmlSchemas)
        {
            foreach(XmlSchema schema in xmlSchemas)
            {
                this.Schemas.Add(schema);
            }
        }

        public void AddSchema(XmlSchema schema)
        {
            this.Schemas.Add(schema);
        }
    }
}
